use axum::{
    extract::{Query, State},
    Json,
};
use serde::Deserialize;
use serde_json::{json, Value};

use crate::{
    stores::{self, redisx},
    utils, SharedState,
};

// 因为小程序审核的限制，现在调整为生成固定参数的永久有效的二维码，用户扫描二维码后，可以进行输入信息。
// 需要实现在数据库存储这个二维码的地址，然后在回复中返回这两个二维码即可。
// 小程序需要实现这个页面。这个页面需要存在。
// 6位数，代表二维码。
// pages/identcode/index 100002 识别码页面
// pages/verifycode/index 100001 核验码页面
// 传参格式为a=100001
// TODO 小程序上报错误信息，使用Post请求，添加认证信息，上传参数为，路径，参数，和错误信息。后期做

// 获取token的参数，grant_type固定为client_credential，appid和appsecret为申请的。正确返回token和expires。
#[derive(Debug, Deserialize)]
pub struct IdentCodeTokenParam {
    pub app_id: String,
    pub app_secret: String,
    pub grant_type: String,
}
pub async fn get_identcode_token(
    State(state): State<SharedState>,
    Query(params): Query<IdentCodeTokenParam>,
) -> Json<Value> {
    let app_id = params.app_id;
    let app_secret = params.app_secret;
    let grant_type = params.grant_type;
    if grant_type != "client_credential" {
        return Json(json!({"code":40002,"msg":"参数不正确"}));
    }
    let pool = &state.db;
    if let Ok(m) = stores::sql::get_appaccount_by_appid(pool, app_id.clone()).await {
        let ok = utils::check_appsecret(&app_secret, &m.appsecret);
        if ok {
            let token = utils::gen_app_token();
            let rs = &state.rs;
            let token_key = format!("token-{}", token);
            if let Err(e) = redisx::set_ex(rs, token_key, app_id.clone(), 7200).await {
                tracing::error!("redis setex error, {} ", e);
                return Json(json!({"code":40003,"msg":"系统错误"}));
            }
            return Json(
                json!({"code":10000,"msg":"获取成功","data":{"token":token,"expires":7200}}),
            );
        }
    }
    Json(json!({"code":40002,"msg":"参数不正确"}))
}
// 识别码的参数，token为自己生成的token，参考微信token,a代表类型，b代表内容。其中a的类型有1001 代表小程序，b传识别码，1002代表公众号图片扫码，b传识别码加密值，1003代表接口，b传识别码
// jwttoken可以用于分享。
#[derive(Debug, Deserialize)]
pub struct IdentCodeConentParam {
    pub token: String,
    pub a: i32,
    pub b: String,
}
// 获取识别码内容，小程序传递识别码，二维码传递识别码的md5值
pub async fn get_identcode_content(
    State(state): State<SharedState>,
    Query(params): Query<IdentCodeConentParam>,
) -> Json<Value> {
    let a = params.a;
    let b = params.b;
    let token = params.token;
    let rs = &state.rs;
    let token_key = format!("token-{}", token);
    let appid = redisx::get(rs, &token_key).await.unwrap_or("".to_string());
    if appid == "" {
        return Json(json!({"code":40002,"msg":"token无效"}));
    }
    match a {
        1001 => {
            let icode_key = format!("icode-{}", b);
            if let Ok(md5str) = redisx::get(rs, &icode_key).await {
                if let Ok(info) = redisx::get(rs, &md5str).await {
                    let _ = redisx::del(rs, &icode_key).await;
                    let _ = redisx::del(rs, &md5str).await;
                    return Json(json!({"code":10000,"msg":"操作成功","data":{"info":info}}));
                }
            }
            return Json(json!({"code":40002,"msg":"识别码已失效"}));
        }
        1002 => {
            if let Ok(info) = redisx::get(rs, &b).await {
                let _ = redisx::del(rs, &b).await;
                return Json(json!({"code":10000,"msg":"操作成功","data":{"info":info}}));
            }
            return Json(json!({"code":40002,"msg":"识别码已失效"}));
        }
        1003 => {
            let icode_key = format!("icode-{}", b);
            if let Ok(md5str) = redisx::get(rs, &icode_key).await {
                if let Ok(info) = redisx::get(rs, &md5str).await {
                    let _ = redisx::del(rs, &icode_key).await;
                    let _ = redisx::del(rs, &md5str).await;
                    return Json(json!({"code":10000,"msg":"操作成功","data":{"info":info}}));
                }
            }
            return Json(json!({"code":40002,"msg":"识别码已失效"}));
        }
        _ => {
            return Json(json!({"code":40002,"msg":"参数不正确"}));
        }
    }
}

// 申请第三方应用账户，参数a为核验码及b为时间戳c为加密字符串，使用sha256算法
#[derive(Debug, Deserialize)]
pub struct ApplyAppAccountParam {
    pub a: String,
    pub b: String,
    pub c: String,
}
pub async fn apply_app_account(
    State(state): State<SharedState>,
    Query(params): Query<ApplyAppAccountParam>,
) -> Json<Value> {
    // 检查时间戳
    let shared_key = &state.conf.mp.shared_key;
    let ecode = params.a;
    let ts = params.b;
    let sign = params.c;
    let ok = utils::check_ecode(&ecode, &ts, shared_key, &sign);
    if !ok {
        return Json(json!({"code":40001,"msg":"验签失败"}));
    }

    let rs = &state.rs;
    let ecode_key = format!("ecode-{}", ecode);
    let openid = redisx::get(rs, &ecode_key).await.unwrap_or("".to_string());
    if openid == "" {
        return Json(json!({"code":40002,"msg":"核验码已失效"}));
    }
    let _ = redisx::del(rs, &ecode_key).await;

    let pool = &state.db;
    if let Ok(m) = stores::sql::get_appaccount_by_openid(pool, openid.clone()).await {
        tracing::error!("app account already existed, {}", m.appid);
        return Json(json!({"code":40005,"msg":"账号已经存在"}));
    }

    let (appid, appsecret) = utils::gen_app_account_id();

    if let Err(e) =
        stores::sql::add_appaccount(pool, openid, appid.clone(), appsecret.clone()).await
    {
        tracing::error!("add app account error, {}", e);
        return Json(json!({"code":40004,"msg":"账号申请失败"}));
    }
    Json(json!({"code":10000,"msg":"成功","data":{"appid":appid,"appsecret":appsecret}}))
}
